
class ChatCacheDB {
  constructor(params) {
    if (!params || typeof params !== 'object') {
      throw new Error('Invalid parameters: must provide configuration object');
    }
 
    // 验证必要参数
    const requiredParams = ['dbName', 'cacheTableName', 'keyPath'];
    for (const param of requiredParams) {
      if (!params[param]) {
        throw new Error(`Missing required parameter: ${param}`);
      }
    }
 
    this.dbName = params.dbName;
    this.cacheTableName = params.cacheTableName;
    this.keyPath = params.keyPath;
    this.indexs = params.indexs || [];
    this.db = null;
    this.indexedDB = this._getIndexedDB();
    this._isOpen = false;
  }
 
  // 私有方法：获取兼容的 indexedDB 实例
  _getIndexedDB() {
    // 1. 浏览器环境
    if (typeof window !== 'undefined' && window.indexedDB) {
      return window.indexedDB;
    }
    // 2. Web Worker 环境
    else if (typeof self !== 'undefined' && self.indexedDB) {
      return self.indexedDB;
    }
    // 3. Node.js 环境（使用 fake-indexeddb）
    else if (typeof require !== 'undefined') {
      try {
        const fakeIndexedDB = require('fake-indexeddb');
        // 同时需要 polyfill 其他相关对象
        if (!global.IDBKeyRange) {
          global.IDBKeyRange = require('fake-indexeddb/lib/FDBKeyRange');
        }
        return fakeIndexedDB;
      } catch (e) {
        console.warn('fake-indexeddb not installed. Run: npm install fake-indexeddb');
        return null;
      }
    }
    // 4. 其他不支持的环境
    return null;
  }
 
  // 检查是否支持 IndexedDB
  isSupported() {
    return !!this.indexedDB;
  }
 
  // 检查数据库是否已打开
  isOpen() {
    return this._isOpen && this.db;
  }
 
  // 打开或创建数据库
  async openDB() {
    if (!this.isSupported()) {
      throw new Error('IndexedDB is not supported in this environment');
    }
 
    if (this.isOpen()) {
      console.warn('Database is already open');
      return this.db;
    }
 
    return new Promise((resolve, reject) => {
      const request = this.indexedDB.open(this.dbName, 1);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Database opening error:', error);
        reject(new Error(`Failed to open database: ${error.message}`));
      };
 
      request.onblocked = () => {
        console.warn('Database opening is blocked');
        reject(new Error('Database opening blocked (maybe another connection is open)'));
      };
 
      request.onsuccess = (event) => {
        this.db = event.target.result;
        this._isOpen = true;
        
        // 监听数据库关闭事件
        this.db.onclose = () => {
          this._isOpen = false;
          console.log('Database connection closed');
        };
        
        // 监听数据库版本变更事件
        this.db.onversionchange = () => {
          console.log('Database version change detected');
          this.db.close();
          this._isOpen = false;
        };
        
        // console.log('Database opened successfully');
        resolve(this.db);
      };
 
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        if (!db.objectStoreNames.contains(this.cacheTableName)) {
          try {
            const objectStore = db.createObjectStore(this.cacheTableName, {
              keyPath: this.keyPath,
              autoIncrement: false
            });
            
            this.indexs.forEach((index) => {
              if (index.name && typeof index.name === 'string') {
                objectStore.createIndex(
                  index.name, 
                  index.path || index.name, // 使用 path 或 name 作为索引路径
                  {
                    unique: !!index.unique
                  }
                );
              }
            });
            console.log('Object store and indexes created successfully');
          } catch (e) {
            console.error('Error creating object store:', e);
            event.target.transaction.abort();
            reject(e);
          }
        }
      };
    });
  }
 
  // 添加或更新数据
  async putData(data) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    if (!data || typeof data !== 'object') {
      throw new Error('Invalid data: must be an object');
    }
 
    if (!data[this.keyPath]) {
      throw new Error(`Data must contain the keyPath property: ${this.keyPath}`);
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readwrite');
      const objectStore = transaction.objectStore(this.cacheTableName);
      const request = objectStore.put(data);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Data put error:', error);
        reject(new Error(`Failed to put data: ${error.message}`));
      };
 
      transaction.oncomplete = () => {
        resolve(data[this.keyPath]); // 返回主键值
      };
 
      transaction.onerror = (event) => {
        const error = event.target.error;
        console.error('Transaction error:', error);
        reject(new Error(`Transaction failed: ${error.message}`));
      };
    });
  }
 
  // 批量添加数据
  async putAllData(items) {
    if (!Array.isArray(items)) {
      throw new Error('Items must be an array');
    }
 
    const results = [];
    for (const item of items) {
      try {
        const result = await this.putData(item);
        results.push(result);
      } catch (e) {
        console.error(`Failed to put item with key ${item[this.keyPath]}:`, e);
        throw e; // 可以选择继续或终止
      }
    }
    return results;
  }
 
  // 根据主键获取数据
  async getData(key) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readonly');
      const objectStore = transaction.objectStore(this.cacheTableName);
      const request = objectStore.get(key);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Data get error:', error);
        reject(new Error(`Failed to get data: ${error.message}`));
      };
 
      request.onsuccess = (event) => {
        resolve(event.target.result || null);
      };
    });
  }
 
  // 根据索引获取数据
  async getDataByIndex(indexName, value) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    if (!indexName || typeof indexName !== 'string') {
      throw new Error('Invalid index name');
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readonly');
      const objectStore = transaction.objectStore(this.cacheTableName);
      
      if (!objectStore.indexNames.contains(indexName)) {
        reject(new Error(`Index ${indexName} does not exist`));
        return;
      }
 
      const index = objectStore.index(indexName);
      const request = index.get(value);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Data get by index error:', error);
        reject(new Error(`Failed to get data by index: ${error.message}`));
      };
 
      request.onsuccess = (event) => {
        resolve(event.target.result || null);
      };
    });
  }
 
  // 获取所有数据
  async getAllData() {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readonly');
      const objectStore = transaction.objectStore(this.cacheTableName);
      const request = objectStore.getAll();
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Get all data error:', error);
        reject(new Error(`Failed to get all data: ${error.message}`));
      };
 
      request.onsuccess = (event) => {
        resolve(event.target.result || []);
      };
    });
  }
 
  // 根据索引获取所有匹配数据
  async getAllDataByIndex(indexName, value) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    if (!indexName || typeof indexName !== 'string') {
      throw new Error('Invalid index name');
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readonly');
      const objectStore = transaction.objectStore(this.cacheTableName);
      
      if (!objectStore.indexNames.contains(indexName)) {
        reject(new Error(`Index ${indexName} does not exist`));
        return;
      }
 
      const index = objectStore.index(indexName);
      const request = index.getAll(value);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Get all data by index error:', error);
        reject(new Error(`Failed to get all data by index: ${error.message}`));
      };
 
      request.onsuccess = (event) => {
        resolve(event.target.result || []);
      };
    });
  }
 
  // 删除数据
  async deleteData(key) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readwrite');
      const objectStore = transaction.objectStore(this.cacheTableName);
      const request = objectStore.delete(key);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('Data delete error:', error);
        reject(new Error(`Failed to delete data: ${error.message}`));
      };
 
      transaction.oncomplete = () => {
        resolve(true);
      };
 
      transaction.onerror = (event) => {
        const error = event.target.error;
        console.error('Transaction error:', error);
        reject(new Error(`Transaction failed: ${error.message}`));
      };
    });
  }
 
  // 清空对象存储
  async clearStore() {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readwrite');
      const objectStore = transaction.objectStore(this.cacheTableName);
      const request = objectStore.clear();
 
      request.onerror = (event) => {
        const error = event.target.error;
        
        reject(new Error(`Failed to clear store: ${error.message}`));
      };
 
      transaction.oncomplete = () => {
        resolve(true);
      };
 
      transaction.onerror = (event) => {
        const error = event.target.error;
        reject(new Error(`Transaction failed: ${error.message}`));
      };
    });
  }
 
  // 关闭数据库
  closeDB() {
    if (this.isOpen()) {
      this.db.close();
      this._isOpen = false;
      console.log('Database closed');
    }
  }
 
  // 更新字段数据
  async updateData(key, updates) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    if (!key) {
      throw new Error('Invalid key: must provide a valid key');
    }
 
    if (!updates || typeof updates !== 'object' || Object.keys(updates).length === 0) {
      throw new Error('Invalid updates: must provide a non-empty object with updates');
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readwrite');
      const objectStore = transaction.objectStore(this.cacheTableName);
      
      // 1. 首先获取现有数据
      const getRequest = objectStore.get(key);
 
      getRequest.onerror = (event) => {
        const error = event.target.error;
        reject(new Error(`更新失败: ${error.message}`));
      };
 
      getRequest.onsuccess = (event) => {
        const existingData = event.target.result;
        
        if (!existingData) {
          reject(new Error(`没有数据: ${key}`));
          return;
        }
 
        // 2. 合并更新到现有数据
        const updatedData = {
          ...existingData,
          ...updates
        };
 
        // 3. 保存更新后的数据
        const putRequest = objectStore.put(updatedData);
 
        putRequest.onerror = (event) => {
          const error = event.target.error;
          reject(new Error(`更新失败: ${error.message}`));
        };
 
        putRequest.onsuccess = () => {
          resolve(true);
        };
      };
 
      transaction.onerror = (event) => {
        const error = event.target.error;
        reject(new Error(`更新失败: ${error.message}`));
      };
    });
  }
 
  /**
   * 根据固定键查询数据，并按主键（时间戳-序列号格式）从小到大排序
   * @param {string} fixedKeyName - 固定键的索引名
   * @param {*} fixedKeyValue - 固定键的值
   * @returns {Promise<Array>} - 按主键排序的结果数组
   */
  async getSortedByPrimaryKey_ischangeKey(fixedKeyName, fixedKeyValue) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    if (!fixedKeyName || typeof fixedKeyName !== 'string') {
      throw new Error('因该使用混合字段');
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readonly');
      const objectStore = transaction.objectStore(this.cacheTableName);
 
      // 检查索引是否存在
      if (!objectStore.indexNames.contains(fixedKeyName)) {
        reject(new Error(`字段 ${fixedKeyName} 不存在`));
        return;
      }
 
      // 使用固定键索引查询
      const fixedKeyIndex = objectStore.index(fixedKeyName);
      
      // 直接使用主键的自然排序
      const request = fixedKeyIndex.openCursor(IDBKeyRange.only(fixedKeyValue));
      
      const results = [];
 
      request.onerror = (event) => {
        const error = event.target.error;
        reject(new Error(`根据字段打开失败: ${error.message}`));
      };
 
      request.onsuccess = (event) => {
        const cursor = event.target.result;
        if (cursor) {
          results.push(cursor.value);
          cursor.continue();
        } else {
          // 由于主键已经是按顺序存储的，理论上不需要再排序
          // 但为了确保顺序正确，可以添加验证逻辑
          const isSorted = results.every((_, i, arr) => 
            i === 0 || arr[i][this.keyPath] >= arr[i-1][this.keyPath]
          );
          
          if (!isSorted) {
          
            results.sort((a, b) => {
              const [aTs, aSeq] = a[this.keyPath].split('-').map(Number);
              const [bTs, bSeq] = b[this.keyPath].split('-').map(Number);
              return aTs - bTs || aSeq - bSeq;
            });
          }

          resolve(results);
        }
      };
    });
  }


/**
 * 获取指定房间的消息（支持按时间筛选和数量限制）
 * @param {string} roomId - 房间ID
 * @param {number} [limit] - 返回的最大消息数量
 * @param {string} [maxTimestamp] - 只返回messages_id小于此值的消息（即比maxTimestamp旧）
 * @returns {Promise<{messages: Array, total: number, oldestMessageId: string|null}>}
 */
async getMessagesByRoom(roomId, limit, maxTimestamp) {
  if (!this.isOpen()) await this.openDB();
  if (!roomId) throw new Error('无效字段');
  return new Promise((resolve, reject) => {
    const transaction = this.db.transaction([this.cacheTableName], 'readonly');
    const objectStore = transaction.objectStore(this.cacheTableName);
    const indexName = 'room_id_timestamp';
    if (!objectStore.indexNames.contains(indexName)) {
      reject(new Error(`Index "${indexName}" does not exist`));
      return;
    }
    const roomTimeIndex = objectStore.index(indexName);
    // 范围设置
    const lowerBound = [roomId, 0];
    const MAX = '\uffff\uffff\uffff\uffff';
    let range;
    if (maxTimestamp !== undefined && maxTimestamp !== null && maxTimestamp !== '') {
      // 获取 messages_id < maxTimestamp 的消息
      range = IDBKeyRange.bound(lowerBound, [roomId, maxTimestamp], false, true);
    } else {
      // 获取最新的消息
      range = IDBKeyRange.bound(lowerBound, [roomId, MAX], false, false);
    }

    let countResult = 0;
    let results = [];
    let resolved = false;

    // 统计总数
    const countRequest = roomTimeIndex.count(range);
    countRequest.onsuccess = () => {
      countResult = countRequest.result;
      if (resolved) {
        // 结果已返回，直接resolve
        resolve({
          messages: results.reverse(), // oldest → newest
          total: countResult,
          oldestMessageId: results.length > 0 ? results[0].messages_id : null,
        });
      }
    };
    countRequest.onerror = () => {
      countResult = 0;
      if (resolved) {
        resolve({
          messages: results.reverse(),
          total: 0,
          oldestMessageId: results.length > 0 ? results[0].messages_id : null,
        });
      }
    };

    // 按时间倒序获取消息（最新的在前面）
    const request = roomTimeIndex.openCursor(range, 'prev');
    request.onerror = (event) => {
      reject(new Error(`获取失败: ${event.target.error?.message}`));
    };
    request.onsuccess = (event) => {
      const cursor = event.target.result;
      if (!cursor) {
        // 游标已结束
        if (typeof countRequest.result === 'number') {
          resolve({
            messages: results.reverse(), // oldest → newest
            total: countRequest.result,
            oldestMessageId: results.length > 0 ? results[0].messages_id : null,
          });
        } else {
          resolved = true;
        }
        return;
      }
      results.push(cursor.value);
      if (limit && results.length >= limit) {
        if (typeof countRequest.result === 'number') {
          resolve({
            messages: results.reverse(),
            total: countRequest.result,
            oldestMessageId: results.length > 0 ? results[0].messages_id : null,
          });
        } else {
          resolved = true;
        }
        return;
      }
      cursor.continue();
    };
  });
}


  /**
   * 根据唯一性索引删除记录
   * @param {string} indexName - 唯一性索引名称
   * @param {*} indexValue - 索引值
   * @returns {Promise<boolean>} - 是否成功删除
   */
  async deleteDataByUniqueIndex(indexName, indexValue) {
    if (!this.isOpen()) {
      await this.openDB();
    }
 
    if (!indexName || typeof indexName !== 'string') {
      throw new Error('字段无效');
    }
 
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([this.cacheTableName], 'readwrite');
      const objectStore = transaction.objectStore(this.cacheTableName);
      
      // 检查索引是否存在
      if (!objectStore.indexNames.contains(indexName)) {
        reject(new Error(`字段 ${indexName} 不存在`));
        return;
      }
 
      const index = objectStore.index(indexName);
      
      // 1. 先通过索引获取主键
      const getRequest = index.get(indexValue);
 
      getRequest.onerror = (event) => {
        const error = event.target.error;
        reject(new Error(`字段获取失败: ${error.message}`));
      };
 
      getRequest.onsuccess = (event) => {
        const record = event.target.result;
        if (!record) {
          resolve(false); // 没有找到记录
          return;
        }
 
        // 2. 使用主键删除记录
        const deleteRequest = objectStore.delete(record[this.keyPath]);
 
        deleteRequest.onerror = (event) => {
          const error = event.target.error;
          console.error('删除错误:', error);
          reject(new Error(`删除错误: ${error.message}`));
        };
 
        deleteRequest.onsuccess = () => {
          resolve(true);
        };
      };
    });
  }
 
  // 删除数据库
  async deleteDB() {
    this.closeDB(); // 先关闭现有连接
 
    return new Promise((resolve, reject) => {
      if (!this.isSupported()) {
        reject(new Error('环境不支持'));
        return;
      }
 
      const request = this.indexedDB.deleteDatabase(this.dbName);
 
      request.onerror = (event) => {
        const error = event.target.error;
        console.error('删除错误', error);
        reject(new Error(`删除失败: ${error.message}`));
      };
 
      request.onsuccess = () => {
        resolve(true);
      };
 
      request.onblocked = () => {
        console.warn('删除数据库被阻塞');
        reject(new Error('删除数据库被阻塞 (可能有其他连接打开)'));
      };
    });
  }
}
 
// 示例配置 - 包含复合索引
const dbConfig = {
  dbName: 'ChatAppDB',
  cacheTableName: 'messages',
  keyPath: 'id',
  indexs: [
    { name: 'roomId', unique: false },
    { name: 'timestamp', unique: false },
    { name: 'userId', unique: false },
    { 
      name: 'room_id_timestamp', 
      path: ['roomId', 'timestamp'], // 复合索引
      unique: false 
    }
  ]
};
 
// 使用示例
async function demo() {
  try {
    // 1. 创建实例
    const chatDB = new ChatCacheDB(dbConfig);
    
    // 2. 检查支持性
    if (!chatDB.isSupported()) {
      return;
    }
    
    // 3. 打开数据库,实际是初始化/打开
    await chatDB.openDB();

    
    // 4. 添加数据
    const message1 = {
      id: 'msg_001',
      roomId: 'room_123',
      userId: 'user_456',
      text: 'Hello world!', 
      timestamp: Date.now()
    };
    
    await chatDB.putData(message1);

    
    // 5. 批量添加
    const messages = [
      { id: 'msg_002', roomId: 'room_123', userId: 'user_789', text: 'Hi there!', timestamp: Date.now() },
      { id: 'msg_003', roomId: 'room_456', userId: 'user_456', text: 'How are you?', timestamp: Date.now() }
    ];
    
    await chatDB.putAllData(messages);
    console.log('Batch save complete');
    
    // 6. 查询数据
    const msg = await chatDB.getData('msg_001');
    console.log('Retrieved message:', msg);
    
    // 7. 按索引查询
    const roomMessages = await chatDB.getAllDataByIndex('roomId', 'room_123');
   
    
    // 8. 使用复合索引查询
    const roomTimeMessages = await chatDB.getMessagesByRoom('room_123', 10, Date.now());
 
    
    // 9. 获取所有数据
    const allMessages = await chatDB.getAllData();

    
    // 10. 删除数据
    await chatDB.deleteData('msg_002');

    
    // 11. 清空存储
    // await chatDB.clearStore();
    
  } catch (error) {

  }
}
 
// 运行示例
// demo();
 
// 导出类
export default ChatCacheDB;
